package org.yaac.client.widget.egql;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.transform;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.yaac.client.i18n.ErrorMessages;
import org.yaac.client.util.DOMUtil;
import org.yaac.client.widget.SortHandler;
import org.yaac.client.widget.SortableColumn;
import org.yaac.shared.egql.ResultCell;
import org.yaac.shared.egql.ResultStatus;
import org.yaac.shared.property.PropertyInfo;

import com.google.common.base.Function;
import com.google.common.base.Joiner;

import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 * 
 */
public class EGQLResultPanel extends Composite {

	private static QueryResultPanelUiBinder uiBinder = GWT.create(QueryResultPanelUiBinder.class);

	interface QueryResultPanelUiBinder extends UiBinder<Widget, EGQLResultPanel> {
	}
	
	@UiField
	Button closeBtn;
	
	@UiField
	DivElement rawStmtDiv;
	
	@UiField
	TextArea statusTxt;

	@UiField(provided = true)
	CellTable<List<ResultCell>> resultTable;

	@UiField
	ScrollPanel resultScroll;
	
	private ListDataProvider<List<ResultCell>> dataProvider;

	private SortHandler<List<ResultCell>> sortHandler;
	
	/**
	 * column map, to make sure same column will not be duplicated
	 */
	private Map<String, SortableColumn<List<ResultCell>, SafeHtml>> colMap;

	private static ErrorMessages errMsgs = GWT.create(ErrorMessages.class);
	
	/**
	 * 
	 */
	public EGQLResultPanel() {
		// init result table
		resultTable = new CellTable<List<ResultCell>>();
		resultTable.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
		resultTable.setVisibleRange(0, Integer.MAX_VALUE);
		
		// init column map
		colMap = new HashMap<String, SortableColumn<List<ResultCell>, SafeHtml>>();
		
		// init sort handler
		sortHandler = new SortHandler<List<ResultCell>>();
		resultTable.addColumnSortHandler(sortHandler);
		
		initWidget(uiBinder.createAndBindUi(this));
		
		// set status text to read only
		statusTxt.setReadOnly(true);
	}

	public EGQLResultPanel withStatement(String rawStmt) {
		// raw statement
		rawStmtDiv.setInnerText(rawStmt);
		rawStmtDiv.setTitle(rawStmt);
		
		return this;
	}

	/**
	 * @param data
	 * @return
	 */
	public EGQLResultPanel appendData(List<List<ResultCell>> data) {
		// it is possible when executing group by quries
		if (data == null || data.isEmpty()) {
			return this;
		}
		
		// dynamic adding columns, any new incoming data may potentially contain not-exist column(s)
		ensureColumns(data);
		
		// lazy initialize data provider, 
		// to make sure there is a waiting icon before first piece of data arrived
		ensureDataProvider();
		
		this.dataProvider.getList().addAll(data);
		this.sortHandler.refresh();
		this.resultScroll.scrollToBottom();
		return this;
	}

	private void ensureDataProvider () {
		if (this.dataProvider == null) {
			dataProvider = new ListDataProvider<List<ResultCell>>();
			dataProvider.addDataDisplay(resultTable);
			sortHandler.setDataProvider(dataProvider);
		}
	}
	
	private void ensureColumns(List<List<ResultCell>> data) {
		for (List<ResultCell> row : data) {
			for (final ResultCell cell : row) {				
				if (!colMap.containsKey(cell.getTitle())) {
					SortableColumn<List<ResultCell>, SafeHtml> column = 
						new SortableColumn<List<ResultCell>, SafeHtml>(new SafeHtmlCell()) {
							@Override
							public SafeHtml getValue(List<ResultCell> cells) {
								ResultCell c = lookupCell(cell.getTitle(), cells);
								
								SafeHtmlBuilder sb = new SafeHtmlBuilder();
								
								if (c == null) {	// not necessary always have this column
									sb.appendHtmlConstant("<br/>");
								} else {
									PropertyInfo property = PropertyInfo.Builder.fromResultCell(c);
									
									// populate warnings in tooltip
									List<String> warnings = property.getWarnings();
									
									if (warnings != null && !warnings.isEmpty()) {
										List<String> warningMsgs = transform(warnings, new Function<String, String>(){
											@Override
											public String apply(String errorCode) {
												return errMsgs.getString(errorCode);
											}
										});
										
										sb.appendHtmlConstant(
												"<div title='" + Joiner.on("<br/>").join(warningMsgs) + "' style='background-color: yellow;'>" + 
												property.asHtml() + "</div>");
									} else {
										sb.appendHtmlConstant(property.asHtml());	
									}
								}
								return sb.toSafeHtml();
							}
					};
					
					resultTable.addColumn(column, cell.getTitle());
					colMap.put(cell.getTitle(), column);
					
					sortHandler.setComparator(column, new Comparator<List<ResultCell>>() {
						@Override
						public int compare(List<ResultCell> cell1s, List<ResultCell> cell2s) {
							// dynamic columns, both c1 and c2 can be null
							ResultCell c1 = lookupCell(cell.getTitle(), cell1s);
							ResultCell c2 = lookupCell(cell.getTitle(), cell2s);
							
							if (c1 == null) {
								if (c2 == null) {
									return 0;
								} else {
									return -1;
								}
							} else {
								if (c2 == null) {
									return 1;
								} else {
									PropertyInfo prop1 = PropertyInfo.Builder.fromResultCell(c1);
									PropertyInfo prop2 = PropertyInfo.Builder.fromResultCell(c2);
									return prop1.compareTo(prop2);
								}
							}
						}
					});
				}
			}
		}
	}
	
	// TODO : make it faster by using map, CAUTION: column orders will get lost by using map 
	private ResultCell lookupCell(String title, List<ResultCell> cells) {
		for (ResultCell cell : cells) {
			if (cell.getTitle().equals(title)) {
				return cell;
			}									
		}
		return null;
	}
	
	/**
	 * @param msg
	 * @return
	 */
	private EGQLResultPanel status(String msg) {
		String curr = statusTxt.getValue();
		String newStr = isNullOrEmpty(curr) ? msg : curr + "\n" + msg;
		
		statusTxt.setText(newStr);
		
		DOMUtil.scrollToBottom(statusTxt.getElement());
		
		return this;
	}

	public void status(ResultStatus status) {
		if (status == null) 
			return;
		
		status(status.toString());
		
		if (status == ResultStatus.FINISHED) {
			// just to remove the waiting icon
			// in case there are no result returned at all
			this.ensureDataProvider();
		}
	}
	
	/**
	 * @param event
	 */
	// TODO : kill backend statements as well
	@UiHandler("closeBtn")
	void onCloseBtnClick(ClickEvent event) {
		this.removeFromParent();
	}
}
